package main

import (
	"fmt"
	"runtime"
	"strings"

	"github.com/go-gl/gl/v4.5-core/gl"
	"github.com/go-gl/glfw/v3.3/glfw"
)

const (
	width  = 600
	height = 500
	rows   = 10
	cols   = 10
)

type cell struct {
	drawable uint32
	x        int
	y        int
}

func initGlfw() *glfw.Window {
	if err := glfw.Init(); err != nil {
		println("glfw 初始化失败")
		panic(err)
	}
	glfw.WindowHint(glfw.Resizable, glfw.False)
	glfw.WindowHint(glfw.ContextVersionMajor, 4)
	glfw.WindowHint(glfw.ContextVersionMinor, 5)
	glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)

	window, err := glfw.CreateWindow(width, height, "coway game", nil, nil)
	if err != nil {
		panic(err)
	}
	window.MakeContextCurrent()

	return window
}

func initOpengl() uint32 {
	if err := gl.Init(); err != nil {
		panic(err)
	}
	version := gl.GoStr(gl.GetString(gl.VERSION))
	println("gl version: ", version)

	vertexSource := `#version 450 core
	in vec3 aPos;
	void main(){
		gl_Position = vec4(aPos, 1.0);
	}
	` + "\x00"

	fragSource := `#version 450 core
	out vec4 frag_colour;
	void main(){
		frag_colour = vec4(1.0, 0.0, 0.0, 0.5);
	}
	` + "\x00"

	compileShader := func(source string, shderType uint32) (shader uint32, err error) {
		// 创建着色器
		shader = gl.CreateShader(shderType)

		// 把go 字符串转为 c 字符串
		csource, free := gl.Strs(source)
		// 附加着色器源码到着色器
		gl.ShaderSource(shader, 1, csource, nil)
		free() // 释放c 字符串内存

		// 编译着色器
		gl.CompileShader(shader)

		var params int32
		gl.GetShaderiv(shader, gl.COMPILE_STATUS, &params)
		if params == gl.FALSE {
			var logLength int32
			gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)

			log := strings.Repeat("\x00", int(logLength+1))

			gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))

			return 0, fmt.Errorf("fail to compile %s : %v", source, log)
		}

		return shader, nil
	}

	shader1, err := compileShader(vertexSource, gl.VERTEX_SHADER)
	if err != nil {
		panic(err)
	}
	shader2, err := compileShader(fragSource, gl.FRAGMENT_SHADER)
	if err != nil {
		panic(err)
	}

	// 创建着色器程序
	program := gl.CreateProgram()
	// 附加shader 到着色器程序
	gl.AttachShader(program, shader1)
	gl.AttachShader(program, shader2)

	gl.LinkProgram(program)

	return program
}

func makeVao(points []float32) uint32 {
	var vbo uint32
	gl.GenBuffers(1, &vbo)
	gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
	gl.BufferData(gl.ARRAY_BUFFER, 4*len(points), gl.Ptr(points), gl.STATIC_DRAW)

	var vao uint32
	gl.GenVertexArrays(1, &vao)
	gl.BindVertexArray(vao)
	gl.EnableVertexAttribArray(0)
	gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, nil)

	return vao

}

func draw(vao, program uint32, lenpoints int) {
	gl.ClearColor(0.5, 0.4, 0.4, 0.5)
	gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)

	gl.UseProgram(program)

	gl.BindVertexArray(vao)
	gl.DrawArrays(gl.TRIANGLES, 0, int32(lenpoints/3))
}

func processIn(win *glfw.Window) {
	win.SetKeyCallback(func(w *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
		println("key:", glfw.GetKeyName(key, 0))
		if key == glfw.KeyEscape && action == glfw.Release {
			win.SetShouldClose(true)
			println("release key: ESCAPE, close program...")
		}
	})
}

func main() {
	runtime.LockOSThread()
	// defer runtime.UnlockOSThread()

	window := initGlfw()
	defer glfw.Terminate()

	program := initOpengl()

	var points = []float32{
		-0.5, 0.5, 0, // top
		-0.5, -0.5, 0, // left
		0.5, -0.5, 0, // right

		0.5, 0.5, 0,
		-0.5, 0.5, 0,
		0.5, -0.5, 0,
	}
	lenpoints := len(points)

	vao := makeVao(points)
	points = nil // opengl使用完就可以释放内存

	for !window.ShouldClose() {
		processIn(window)
		draw(vao, program, lenpoints)

		glfw.PollEvents()
		window.SwapBuffers()
	}

}
